package com.bening.smsapp.bootstrap;

import com.bening.smsapp.bean.FieldValueRuleBean;
import com.bening.smsapp.bean.MachineSource;
import com.bening.smsapp.bean.PathSource;
import com.bening.smsapp.constant.PropertiesConstant;
import com.bening.smsapp.constant.SmsAppConstant;
import com.bening.smsapp.factory.SmsAppFactory;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.Logger;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;


public class BootConfigParameters
{
    private static String dbJdbcClass;
    private static String dbUri;
    private static String dbUser;
    private static String dbPass;
    private static int maxActive;
    private static int maxIdle;
    private static int maxWait;
    private static int maxOpenPreparedStatements;
    private static String dtdName;
    private static String rootName;
    private static String tempDir;
    private static String daoImpl;
    private static long threadWait;
    private static int threadWorker;
    private static int threadIdle;
    private static String sqlldrCmd;
    private static String asciiCtlTemplate;
    private static String asn1CtlTemplate;
    
    private static Properties parserProperties;
    private static Properties flaggingRuleProperties;
    private static Properties downloadProperties;
    private static Properties registerProperties;
    private static Properties queryProperties;
    private static Properties xmlProperties;
    private static boolean isConfigLoaded = false;
    
    private static List asciiParserRule;
    private static List machineSourceList;
    
    final static Logger logger = Logger.getLogger( BootConfigParameters.class );
    static Properties prop;

    // this will hold the only instance of this  class
    static BootConfigParameters thisInstance = null;

    private BootConfigParameters()
    {
    	asciiParserRule = new ArrayList();
    	machineSourceList = new ArrayList();
    }

    /**
     * the only way to create an instance of this singleton
     * @return the singleton object
     */
    public static synchronized BootConfigParameters getInstance() {
    	if(thisInstance!=null)
    		return thisInstance;
    	else
    		return null;
    }
    public static synchronized BootConfigParameters getInstance(String filepath)
    {
        
        final String METHOD_SIGNATURE = "[SmsApp-Bootstrap]";
        // load log4j properties
        PropertyConfigurator.configure(filepath+System.getProperty("file.separator")+ "log4j.properties");
        final Logger logger = Logger.getLogger( BootConfigParameters.class );
        if( thisInstance == null )
        {
            if ( logger.isDebugEnabled() )
            {
                logger.debug( METHOD_SIGNATURE + "creating BootstrapParameters instance and loading properties." );
            }

            thisInstance = new BootConfigParameters();

            logger.info("Reading config from "+filepath+"/"+SmsAppConstant.CORE_CONFIGNAME);
            // load global properties
            thisInstance.loadGlobalProperties(filepath+"/"+SmsAppConstant.CORE_CONFIGNAME);
            // load query properties
            thisInstance.loadQueryProperties(filepath+"/"+SmsAppConstant.SQL_CONFIGNAME);
            // load app properties
            thisInstance.loadAppProperties();
            // load configuration for remote machine
            thisInstance.loadMachineSourcesProperties();
            // load rule
            thisInstance.generateParseRule();
            // load flagging rule
            thisInstance.loadFlaggingRuleProperties();
            // all properties loaded succesfully
            isConfigLoaded = true;
            // load xml parser properties
            thisInstance.loadXmlParserProperties(filepath+"/"+SmsAppConstant.CORE_CONFIGNAME);
            // load ctl template in we use sl loader
            if(thisInstance.daoImpl.equals(SmsAppConstant.SQLLDR_IMPL)) {
            	thisInstance.loadCtlTemplate(filepath+"/");
            }
            	
        }

        return thisInstance;
    }
    
    private void loadCtlTemplate(String filePath) {
    	// load ctl template here
    	try {
    		// loading ascii template
	    	BufferedReader reader = new BufferedReader( new FileReader( filePath+SmsAppConstant.ASCII_CTL_TEMPLATE ) );
	        String currentLine = reader.readLine();
	        StringBuffer sb = new StringBuffer();
            while( currentLine != null )
            {
                sb.append(currentLine);
                sb.append(System.getProperty("line.separator"));
                currentLine = reader.readLine();
            }
            reader.close();
            this.asciiCtlTemplate = sb.toString();
            //loading asn1 template
            reader = new BufferedReader( new FileReader( filePath+SmsAppConstant.ASN1_CTL_TEMPLATE ) );
	        currentLine = reader.readLine();
	        sb = new StringBuffer();
            while( currentLine != null )
            {
                sb.append(currentLine);
                sb.append(System.getProperty("line.separator"));
                currentLine = reader.readLine();
            }
            reader.close();
            this.asn1CtlTemplate = sb.toString();
    	}catch(FileNotFoundException f) {
    		logger.error(f);
    	}catch(IOException i) {
    		logger.error(i);
    	}
    }

    
    public String getSqlldrCmd() {
		return sqlldrCmd;
	}

	/**
     * load flagging rule properties that is needed
     */
    private void loadXmlParserProperties(String filePath) {
    	xmlProperties = new Properties();
        InputStream in;
        
        try
        {

            in = new FileInputStream(filePath);
            xmlProperties.load( in );
        }
        catch( FileNotFoundException e )
        {
            logger.error( "The property file: '" +  filePath + "' not found." ,e);
            System.exit( 5 );
        }
        catch( IOException e )
        {
            logger.error( "IOException: error reading properties file: '" +filePath , e );
            System.exit( 6 );
        }

        
    }
    /**
     * load flagging rule properties that is needed
     */
    private void loadFlaggingRuleProperties() {
        PreparedStatement prepStmt = null;
        Connection conn;
        ResultSet rs = null;
        flaggingRuleProperties = new Properties();
        
        conn = SmsAppFactory.getDbConnection();
        try {
        	prepStmt = conn.prepareStatement(this.queryProperties.getProperty(PropertiesConstant.SQL_SELECT_FLAGGING_RULE));
        	rs = prepStmt.executeQuery();
        	while(rs.next()) {
        		flaggingRuleProperties.setProperty(rs.getString("PREFIX"), rs.getString("FLAG_NAME"));        		
        	}
        	rs.close();
        	prepStmt.close();
        	conn.close();
        	
        }catch(SQLException s) {
        	logger.error("Error getting database connection ...", s);
        }    	
    }
    /**
     * load machine source properties that is needed
     */
    private void loadMachineSourcesProperties() { 
        PreparedStatement prepStmt = null;
        Connection conn;
        ResultSet rs = null;
        
        conn = SmsAppFactory.getDbConnection();
        try {
        	String sql = this.queryProperties.getProperty(PropertiesConstant.SQL_SELECT_SOURCE_MACHINE);
        	prepStmt = conn.prepareStatement(sql);
        	rs = prepStmt.executeQuery();
        	// remote machine
        	while(rs.next()) {
        		MachineSource m = new MachineSource();
        		m.setIpAddress(rs.getString("MACHINE_IP"));
        		m.setMachineId(rs.getString("MACHINE_ID"));
        		m.setMachineSeq(rs.getInt("MACHINE_SEQUENCE"));
        		
        		m.setPassword(rs.getString("MACHINE_PASSWD"));
        		m.setPort(rs.getInt("MACHINE_PORT"));
        		m.setUsername(rs.getString("MACHINE_USERNAME"));
        		m.setMachineType(rs.getInt("MACHINE_TYPE"));
        		
        		String sql2 = this.queryProperties.getProperty(PropertiesConstant.SQL_SELECT_SOURCE_LIST);
        		PreparedStatement prepStmt2 = conn.prepareStatement(sql2);
        		prepStmt2.setString(1, m.getMachineId());
        		
        		ResultSet rs2 = prepStmt2.executeQuery();
        		List pathList = new ArrayList();
        		while(rs2.next()) {
        			PathSource p = new PathSource();
        			p.setCompressType(rs2.getInt("COMPRESS_TYPE"));
        			p.setPathDir(rs2.getString("FILEPATH"));
        			p.setLocalPathDir(rs2.getString("LOCAL_FILEPATH"));
        			p.setFileNamePattern(rs2.getString("FILENAME_FILTER"));
        			p.setPathSourceId(rs2.getInt("SOURCEPATH_ID"));
        			p.setNodeId(rs2.getString("NODE_ID"));
        			p.setCdrType(rs2.getInt("CDR_TYPE"));
        			pathList.add(p);
        		}
        		rs2.close();
        		prepStmt2.close();
        		
        		m.setSourcePathList(pathList);
        		machineSourceList.add(m);
        	}
        	rs.close();
        	prepStmt.close();
        	conn.close();
        	
        }catch(SQLException s) {
        	logger.error("Error getting database connection in loadMachineSourcesProperties()...", s);
        }

    }

    /**
     * load app Properties that is needed
     */
    private void loadAppProperties() {
        PreparedStatement prepStmt = null;
        Connection conn;
        ResultSet rs = null;
        downloadProperties = new Properties();
        parserProperties = new Properties();
        registerProperties = new Properties();
        
        conn = SmsAppFactory.getDbConnection();
        try {
        	
        	prepStmt = conn.prepareStatement(this.queryProperties.getProperty(PropertiesConstant.SQL_SELECT_CONFIGVALUE));
        	rs = prepStmt.executeQuery();
        	while(rs.next()) {
        		
        		if(rs.getInt("CONFIG_TYPE")==PropertiesConstant.CONFIG_DOWNLOAD) {      		
        			downloadProperties.setProperty(rs.getString("CONFIG_NAME"), rs.getString("CONFIG_VALUE"));
        		}else if(rs.getInt("CONFIG_TYPE")==PropertiesConstant.CONFIG_PARSER) {
        			parserProperties.setProperty(rs.getString("CONFIG_NAME"), rs.getString("CONFIG_VALUE"));        			
        		}else if(rs.getInt("CONFIG_TYPE")==PropertiesConstant.CONFIG_REGISTER){ // future use
        			registerProperties.setProperty(rs.getString("CONFIG_NAME"), rs.getString("CONFIG_VALUE"));
        		}else {
        			
        		}
        	}
        	rs.close();
        	prepStmt.close();
        	conn.close();
        	
        }catch(SQLException s) {
        	logger.error("Error getting database connection ...", s);
        }

    }
    /**
     * load all query properties that is needed
     */
    private void loadQueryProperties(String filePath)
    {
    	prop = new Properties();
        InputStream in;
        
        try
        {

            in = new FileInputStream(filePath);
            prop.load( in );
        }
        catch( FileNotFoundException e )
        {
            logger.error( "The property file: '" +  filePath + "' not found." ,e);
            System.exit( 5 );
        }
        catch( IOException e )
        {
            logger.error( "IOException: error reading properties file: '" +filePath , e );
            System.exit( 6 );
        }
        this.queryProperties = prop;
    }
    /**
     * load all global properties that is needed
     */
    private void loadGlobalProperties(String filePath)
    {
    	prop = new Properties();
        InputStream in;
        
        try
        {

            in = new FileInputStream(filePath);
            prop.load( in );
        }
        catch( FileNotFoundException e )
        {
            logger.error( "The property file: '" +  filePath + "' not found." ,e);
            System.exit( 5 );
        }
        catch( IOException e )
        {
            logger.error( "IOException: error reading properties file: '" +filePath , e );
            System.exit( 6 );
        }

        // put properties into this object's attributes
        // database connection related properties
        this.dbJdbcClass   = prop.getProperty( PropertiesConstant.PROP_DB_JDBC_CLASS );
        this.dbUri         = prop.getProperty( PropertiesConstant.PROP_DB_URI );
        this.dbUser        = prop.getProperty( PropertiesConstant.PROP_DB_USER );
        this.dbPass        = prop.getProperty( PropertiesConstant.PROP_DB_PASS );

        this.maxActive	   = Integer.parseInt(prop.getProperty( PropertiesConstant.PROP_MAX_ACTIVE ));
        this.maxIdle       = Integer.parseInt(prop.getProperty( PropertiesConstant.PROP_MAX_IDLE ));
        this.maxOpenPreparedStatements = Integer.parseInt(prop.getProperty( PropertiesConstant.PROP_MAX_PREP_STMT ));
        this.maxWait       = Integer.parseInt(prop.getProperty( PropertiesConstant.PROP_MAX_WAIT ));

        this.rootName = prop.getProperty(PropertiesConstant.PROP_ASN1_ROOTNAME);
        this.dtdName  = prop.getProperty(PropertiesConstant.PROP_ASN1_DTD);
        this.tempDir  = prop.getProperty(PropertiesConstant.PROP_TEMP_DIR);
        this.daoImpl = prop.getProperty(PropertiesConstant.PROP_DAO_IMPL);
        
        this.threadWorker = Integer.parseInt(prop.getProperty(PropertiesConstant.PROP_THREAD_WORKER));
        this.threadWait = Integer.parseInt(prop.getProperty(PropertiesConstant.PROP_THREAD_WAIT));
        this.threadIdle = Integer.parseInt(prop.getProperty(PropertiesConstant.PROP_THREAD_IDLE));
        this.sqlldrCmd	= prop.getProperty(PropertiesConstant.PROP_SQLLDR_CMD);
        
    }


    public String getDbUri()
    {
        return dbUri;
    }

    public String getDbJdbcClass()
    {
        return dbJdbcClass;
    }

    public String getDbUser()
    {
        return dbUser;
    }

    public String getDbPass()
    {
        return dbPass;
    }


	public boolean isConfigLoaded() {
		return isConfigLoaded;
	}

	public Properties getDownloadProperties() {
		return downloadProperties;
	}

	public void setDownloadProperties(Properties downloadProperties) {
		this.downloadProperties = downloadProperties;
	}

	public int getMaxActive() {
		return maxActive;
	}

	public void setMaxActive(int maxActive) {
		this.maxActive = maxActive;
	}

	public int getMaxIdle() {
		return maxIdle;
	}

	public void setMaxIdle(int maxIdle) {
		this.maxIdle = maxIdle;
	}

	public int getMaxOpenPreparedStatements() {
		return maxOpenPreparedStatements;
	}

	public void setMaxOpenPreparedStatements(int maxOpenPreparedStatements) {
		this.maxOpenPreparedStatements = maxOpenPreparedStatements;
	}

	public int getMaxWait() {
		return maxWait;
	}

	private void generateParseRule() {

		String ruleAscii = parserProperties.getProperty(PropertiesConstant.CORE_PARSING_RULE_ASCII);
		logger.debug(ruleAscii);
		String[] ruleArrayAscii = ruleAscii.split("\\,");
		for(int i=0;i<ruleArrayAscii.length;i++) {
			String[] ruleValue = ruleArrayAscii[i].split("\\=");
			FieldValueRuleBean f = new FieldValueRuleBean();
			f.setNameRule(ruleValue[0]);
			f.setValueRule(Integer.valueOf(ruleValue[1]).intValue());
			asciiParserRule.add(i,f);
		}
		
	}


	public Properties getQueryProperties() {
		return queryProperties;
	}


	public static List getAsciiParserRule() {
		return asciiParserRule;
	}

	public static List getMachineSourceList() {
		return machineSourceList;
	}

	public Properties getFlaggingRuleProperties() {
		return flaggingRuleProperties;
	}


	public Properties getRegisterProperties() {
		return registerProperties;
	}

	public String getDtdName() {
		return dtdName;
	}

	public String getRootName() {
		return rootName;
	}

	public Properties getParserProperties() {
		return parserProperties;
	}

	public String getTempDir() {
		return tempDir;
	}

	public static Properties getXmlProperties() {
		return xmlProperties;
	}

	public String getDaoImpl() {
		return daoImpl;
	}

	public int getThreadIdle() {
		return threadIdle;
	}

	public long getThreadWait() {
		return threadWait;
	}

	public int getThreadWorker() {
		return threadWorker;
	}

	public static String getAsciiCtlTemplate() {
		return asciiCtlTemplate;
	}

	public static String getAsn1CtlTemplate() {
		return asn1CtlTemplate;
	}

}
